/*
  Losu Copyright Notice
  --------------------
    Losu is an open source programming language project under the MIT license
  that can be used for both academic and commercial purposes. There are no
  fees, no royalties, and no GNU-like "copyright" restrictions. Losu qualifies
  as open source software. However, Losu is not public property, and founder
  'chen-chaochen' retains its copyright.

    Losu has been registered with the National Copyright Administration of the
  People's Republic of China, and adopts the MIT license as the copyright
  licensing contract under which the right holder conditionally licenses its
  reproduction, distribution, and modification rights to an unspecified public.

    If you use Losu, please follow the public MIT agreement or choose to enter
  into a dedicated license agreement with us.

  The MIT LICENSE is as follows
  --------------------
  Copyright  2020 - now  chen-chaochen

    Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the “Software”), to
  deal in the Software without restriction, including without limitation the
  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  sell copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/
/**
 * Losu API
 */

#include "losu_api.h"
#include "losu_bytecode.h"
#include "losu_errmsg.h"
#include "losu_gc.h"
#include "losu_malloc.h"
#include "losu_object.h"
#include "losu_syntax.h"
#include "losu_vm.h"
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef define_source_losu_api
#define define_source_losu_api

/* Vm  APIs */

/**
 * @brief Create a new LosuVm
 * @param stacksize The size of the stack
 * @return LosuVm* A pointer to the new LosuVm
 */
LosuExtern LosuVm *
vm_create (int32_t size)
{
  __longjump lj = (__longjump){
    .err = 0,
    .pre = NULL,
  };
  if (size <= 0)
    return NULL;
  LosuVm *vm = __losu_mem_new (NULL, LosuVm);
  if (vm == NULL)
    return NULL;
  *vm = (LosuVm){
    .mainthread = vm,
    .stack = NULL,
    .strpool = (__vmStrseg){
        .size = 0,
        .nsize = 0,
        .strobj = NULL,
    },
    .bufftmp = NULL,
    .nbufftmp = 0,
    .inspool = NULL,
    .funcpool = NULL,
    .hashpool = NULL,
    .nblocks = sizeof(LosuVm),
    .errjmp = &lj,
    .emjmp = NULL,
    .gcDymax = 1,
    .gcMax = SIZE_MAX,
    .gcHook = 0,
    .name = NULL,
  };

  LosuObj *oldbase = vm->base;
  LosuObj *oldtop = vm->top;
  if (setjmp (lj.jmpflag) == 0)
    {
      vm->global = __losu_objUnit_new (vm, 0);
      __losu_vmHeap_init (vm, size);
      __losu_objStringPool_init (vm);
      __losu_syntaxLex_init (vm);
    }
  else
    {
      vm->base = oldbase;
      vm->top = oldtop;
      if (vm->top - vm->stack < vm->stacksize - 1)
        vm->stackmax = vm->stack + (vm->stacksize - 1);
    }

  vm->errjmp = lj.pre;

  if (lj.err != 0)
    {
      vm_close (vm);
      return NULL;
    }

  return vm;
}

/**
 * @brief Initialize a LosuVm, Load all inline-functions
 * @param vm A pointer to the LosuVm
 * @return int 0 if successful, non-zero otherwise
 */
LosuExtern int32_t vm_init (LosuVm *vm,int32_t argc,const char** argv);

/**
 * @note This API is deprecated and it is recommended to use the alternative
 * option in Losu API 2.0
 * @brief Fork a LosuVm, The forked VM shares a global variable table with the
 * original VM, but has a different Runspace, Local variables, and Stack.
 * @param vm A pointer to the original LosuVm
 * @param size The size of the stack
 * @return LosuVm* A pointer to the new LosuVm
 */
LosuExtern LosuVm *
vm_fork (LosuVm *oldvm, int32_t size)
{
  __longjump lj = (__longjump){
    .err = 0,
    .pre = NULL,
  };
  if (size <= 0)
    return NULL;
  LosuVm *vm = __losu_mem_new (NULL, LosuVm);
  if (vm == NULL)
    return NULL;
  *vm = (LosuVm){
    .mainthread = oldvm,
    .stack = NULL,
    .strpool = (__vmStrseg){
        .size = 0,
        .nsize = 0,
        .strobj = NULL,
    },
    .bufftmp = NULL,
    .nbufftmp = 0,
    .inspool = NULL,
    .funcpool = NULL,
    .hashpool = NULL,
    .nblocks = sizeof(LosuVm),
    .errjmp = &lj,
    .emjmp = NULL,
    .gcDymax = 1,
    .gcMax = SIZE_MAX,
    .gcHook = 0,
  };

  LosuObj *oldbase = vm->base;
  LosuObj *oldtop = vm->top;
  if (setjmp (lj.jmpflag) == 0)
    {
      vm->global = oldvm->global;
      __losu_vmHeap_init (vm, size);
      __losu_objStringPool_init (vm);
      __losu_syntaxLex_init (vm);
    }
  else
    {
      vm->base = oldbase;
      vm->top = oldtop;
      if (vm->top - vm->stack < vm->stacksize - 1)
        vm->stackmax = vm->stack + (vm->stacksize - 1);
    }

  vm->errjmp = lj.pre;

  if (lj.err != 0)
    {
      vm_close (vm);
      return NULL;
    }

  return vm;
}

/**
 * @brief Destroy a LosuVm, Exit with 0, adn jump to caller function.
 * @param vm A pointer to the LosuVm
 */
LosuExtern void
vm_stop (LosuVm *vm)
{
  __losu_vmHeap_break (vm, 0);
}

/**
 * @brief Set LosuVm Error, Exit with ecode, adn jump to caller function.
 * @param vm A pointer to the LosuVm
 * @param ecode Error Code
 * @param estr Error String
 */
LosuExtern void
vm_error (LosuVm *vm, int32_t ecode, const char *estr, ...)
{

  memset (vm->staticbuff, 0, sizeof (vm->staticbuff));
  va_list ap;
  va_start (ap, estr);
  vsprintf ((char *)(vm->staticbuff), estr, ap);

  char *tmp = charset_toSys ((const char *)vm->staticbuff);
  if (tmp == NULL)
    tmp = (char *)vm->staticbuff;

  fprintf (stderr, "Runtime Error: %s\n at line %d of '%s'\n", tmp,
           __losuApigetline (vm, vm->top - 1),
           vm->name != NULL ? vm->name : "<unknown>");

  /* if not Native UTF8, free block */
  if (tmp != (char *)(vm->staticbuff))
    __losu_mem_free (NULL, tmp);

  va_end (ap);
  __losu_vmHeap_break (vm, ecode);
}

/**
 * @brief Set LosuVm Warning, Not exit
 * @param vm A pointer to the LosuVm
 * @param estr Error String
 */
LosuExtern void
vm_warning (LosuVm *vm, const char *estr, ...)
{
  memset (vm->staticbuff, 0, sizeof (vm->staticbuff));
  va_list ap;
  va_start (ap, estr);
  vsprintf ((char *)(vm->staticbuff), estr, ap);

  char *tmp = charset_toSys ((const char *)vm->staticbuff);
  if (tmp == NULL)
    tmp = (char *)vm->staticbuff;

  fprintf (stderr, "Runtime Warning: %s\n at line %d of '%s'\n", tmp,
           __losuApigetline (vm, vm->top - 1),
           vm->name != NULL ? vm->name : "<unknown>");

  /* if not Native UTF8, free block */
  if (tmp != (char *)(vm->staticbuff))
    __losu_mem_free (NULL, tmp);

  va_end (ap);
}

/**
 * @brief Load a Losu file, and execute it.
 * @param vm A pointer to the LosuVm
 * @param filename The name of the file
 * @return int32_t 0 if successful, non-zero otherwise
 */
LosuExtern int32_t
vm_dofile (LosuVm *vm, const char *f)
{
  int32_t i;
  if (!(i = vm_loadfile (vm, f)))
    return vm_execute (vm, 0, -1, f);
  return i;
}

/**
 * @brief Load a string, and execute it .
 * @param vm A pointer to the LosuVm
 * @param str The Losu string
 * @param n The name of the string
 * @return int32_t 0 if successful, non-zero otherwise
 */
LosuExtern int32_t
vm_dostring (LosuVm *vm, const char *s, const char *n)
{
  int32_t i;
  if (!(i = vm_loadstring (vm, s, n)))
    return vm_execute (vm, 0, -1, n);
  return i;
}

/**
 * @brief Load Byte-code, and execute it.
 * @param vm A pointer to the LosuVm
 * @param byte The Byte-code
 * @param n The name of the Byte-code
 * @return int32_t 0 if successful, non-zero otherwise
 */
LosuExtern int32_t
vm_dobyte (LosuVm *vm, const char *byte, size_t len, const char *name)
{
  int32_t i;
  if (!(i = vm_loadbyte (vm, byte, len, name)))
    return vm_execute (vm, 0, -1, name);
  return i;
}

/**
 * @brief Load a Losu file, `NOT` execute it.
 * @param vm A pointer to the LosuVm
 * @param filename The name of the file
 * @return int32_t 0 if successful, non-zero otherwise
 */
int32_t
vm_loadfile (LosuVm *vm, const char *f)
{
  return __losu_syntaxIO_loadfile (vm, f);
}

/**
 * @brief Load a string, `NOT` execute it.
 * @param vm A pointer to the LosuVm
 * @param str The Losu string
 * @return int32_t 0 if successful, non-zero otherwise
 */
LosuExtern int32_t
vm_loadstring (LosuVm *vm, const char *s, const char *name)
{
  return __losu_syntaxIO_loadstring (vm, s, strlen (s), name);
}

/**
 * @brief Load Byte-code, `NOT` execute it.
 * @param vm A pointer to the LosuVm
 * @param byte The Byte-code
 * @param len The length of the Byte-code
 * @param name The name of the Byte-code
 */
LosuExtern int32_t
vm_loadbyte (LosuVm *vm, const char *byte, size_t len, const char *name)
{
  return __losu_syntaxIO_loadstring (vm, byte, len, name);
}

/**
 * @brief In 'Protected Mode', call a function on the stack and specify the
 * number of arguments and return values."Protected Mode' creates a protected
 * call stack for each call and jumps to the top of that stack when an error is
 * thrown
 * @param vm A pointer to the LosuVm
 * @param narg The number of arguments
 * @param nres The number of return values
 * @param name The name of the function
 * @return int 0 if successful, non-zero otherwise
 */
int32_t
vm_execute (LosuVm *vm, int32_t narg, int32_t nres, const char *name)
{
  const char *on = vm->name;
  vm->name = name;
  int32_t i = __losu_vmHeap_callS (vm, narg, nres);
  vm->name = on;
  return i;
}

/**
 * @brief Get a value from the global variable table in vm.
 * @param vm A pointer to the LosuVm
 * @param name The name of the variable
 * @return LosuObj* A pointer to the value
 */
LosuExtern LosuObj *
vm_getval (LosuVm *vm, const char *name)
{
  return (LosuObj *)__losu_objUnit_getstr (vm->global,
                                           __losu_objString_new (vm, name));
}

/**
 * @brief Set a value in the global variable table in vm.
 * @param vm A pointer to the LosuVm
 * @param name The name of the variable
 * @param val The value to set
 */
LosuExtern void
vm_setval (LosuVm *vm, const char *name, LosuObj val)
{
  LosuObj key = (LosuObj){
    .type = LosuTypeDefine_string,
    .value.str = __losu_objString_new (vm, name),
  };
  *__losu_objUnit_set (vm, vm->global, &key) = val;
}

/**
 * @brief Closes the specified LosuVm, reclaiming all the memory space it
 * belongs to; only the 'Root VM' reclaims the global variable table
 * @param vm A pointer to the LosuVm
 */
LosuExtern void
vm_close (LosuVm *vm)
{
  if (vm->mainthread != vm)
    vm->global = NULL;

  __losu_gc_collect (vm, 1);
  __losu_objStringPool_deinit (vm);

  if (vm->stack)
    {
      __losu_mem_free (vm, vm->stack);
      vm->nblocks -= (vm->stackmax - vm->stack + 1) * sizeof (LosuObj);
    }
  if (vm->bufftmp)
    {
      __losu_mem_free (vm, vm->bufftmp);
      vm->nblocks -= vm->nbufftmp * sizeof (char);
    }
  __losu_mem_free (vm, vm);
}

/* GC APIs */

/**
 * @brief Set the maximum amount of memory for the GC
 * @param vm A pointer to the LosuVm
 * @param size The maximum amount of memory for the GC
 */
LosuExtern void
gc_setmax (LosuVm *vm, _l_gcint size)
{
  vm->gcMax = vm->gcDymax = size;
}

/**
 * @brief Get the amount of memory used by the GC
 * @param vm A pointer to the LosuVm
 * @return _l_gcint The amount of memory used by the GC
 */
LosuExtern _l_gcint
gc_getmemNow (LosuVm *vm)
{
  return vm->nblocks;
}

/**
 * @brief Get the maximum amount of memory for the GC
 * @param vm A pointer to the LosuVm
 * @return _l_gcint The maximum amount of memory for the GC
 */
LosuExtern _l_gcint
gc_getmemMax (LosuVm *vm)
{
  return vm->gcHook;
}

/**
 * @brief Collect the garbage
 * @param vm A pointer to the LosuVm
 * @return _l_bool 1 if collection operation performed, 0 if collection
 * operation not performed
 */
LosuExtern _l_bool
gc_collect (LosuVm *vm)
{
  return __losu_gc_checkClt (vm);
}

/* Arg APIs */

/**
 * @brief Get an argument from the call-stack
 * @param vm A pointer to the LosuVm
 * @param idx The index of the argument
 * @return LosuObj* A pointer to the argument
 */
LosuExtern LosuObj *
arg_get (LosuVm *vm, int idx)
{
  if (idx > 0)
    {
      LosuObj *o = vm->base + (idx - 1);
      if (o >= vm->top)
        ovtype (o) = LosuTypeDefine_null;
      return o;
    }
  return (LosuObj *)(&_inlineNullObj);
}

/**
 * @brief Return an argument from the call-stack
 * @param vm A pointer to the LosuVm
 * @param obj The argument to return
 */
LosuExtern void
arg_return (LosuVm *vm, LosuObj obj)
{
  *(vm->top) = (obj);
  if (vm->top == vm->stackmax)
    vm_error (vm, LosuErrorCode_Heap,
              __config_losucore_errmsg_msgStackOverflow);
  vm->top++;
}

/**
 * @brief Get the number of arguments in the call-stack
 * @param vm A pointer to the LosuVm
 * @return int32_t The number of arguments
 */
LosuExtern int32_t
arg_num (LosuVm *vm)
{
  return (int32_t)(vm->top - vm->base);
}

/**
 * @brief Get an number type (_l_number) argument from the call-stack
 * @param vm A pointer to the LosuVm
 * @param idx The index of the argument
 * @return _l_number The argument
 */
/* Arg_get APIs */
LosuExtern _l_number
arg_getnum (LosuVm *vm, int idx)
{
  return obj_tonum (vm, arg_get (vm, idx));
}

/**
 * @brief Get an string type (const char*) argument from the call-stack
 * @param vm A pointer to the LosuVm
 * @param idx The index of the argument
 * @return const char* The argument
 */
LosuExtern const char *
arg_getstr (LosuVm *vm, int idx)
{
  return obj_tostr (vm, arg_get (vm, idx));
}

/**
 * @brief Get a pointer type (char*) argument from the call-stack
 * @param vm A pointer to the LosuVm
 * @param idx The index of the argument
 * @return char* The argument
 */
LosuExtern char *
arg_getptr (LosuVm *vm, int idx)
{
  return obj_toptr (vm, arg_get (vm, idx));
}

/**
 * @brief Get a function type (LosuApi) argument from the call-stack
 * @param vm A pointer to the LosuVm
 * @param idx The index of the argument
 * @return LosuApi The argument
 */
LosuExtern LosuApi
arg_getfunc (LosuVm *vm, int idx)
{
  return obj_tofunction (vm, arg_get (vm, idx));
}

/**
 * @brief Get the specified parameter type (Defined value) from the call-stack,
 * @param vm A pointer to the LosuVm
 * @param idx The index of the argument
 * @return int The type of the argument
 */
LosuExtern int
arg_gettype (LosuVm *vm, int idx)
{
  return obj_type (vm, arg_get (vm, idx));
}

/**
 * @brief Get the specified parameter type (Defined value) from the call-stack,
 * @param vm A pointer to the LosuVm
 * @param idx The index of the argument
 * @return const char* The type of the argument
 */
LosuExtern const char *
arg_gettypeStr (LosuVm *vm, int idx)
{
  return obj_typeStr (vm, arg_get (vm, idx));
}

/**
 * @brief Return a number type (_l_number) argument to the call-stack
 * @param vm A pointer to the LosuVm
 * @param num The argument to return
 */
/* Arg_return APIs */
LosuExtern void
arg_returnnum (LosuVm *vm, _l_number num)
{
  arg_return (vm, obj_newnum (vm, num));
}

/**
 * @brief Return a string type (const char*) argument to the call-stack
 * @param vm A pointer to the LosuVm
 * @param str The argument to return
 */
LosuExtern void
arg_returnstr (LosuVm *vm, const char *str)
{
  arg_return (vm, obj_newstr (vm, (char *)str));
}

/**
 * @brief Return a string type (const char*) argument to the call-stack
 * @param vm A pointer to the LosuVm
 * @param str The argument to return
 * @param len The length of the string
 */
LosuExtern void
arg_returnstrlen (LosuVm *vm, const char *str, size_t len)
{
  arg_return (vm, obj_newstrlen (vm, (char *)str, len));
}

/**
 * @brief Return a cAPIfunction type (LosuApi) argument to the call-stack
 * @param vm A pointer to the LosuVm
 * @param func The argument to return
 */
LosuExtern void
arg_returnfunc (LosuVm *vm, LosuApi func)
{
  arg_return (vm, obj_newfunction (vm, func));
}

/**
 * @brief Return a pointer type (char*) argument to the call-stack
 * @param vm A pointer to the LosuVm
 * @param ptr The argument to return
 */
LosuExtern void
arg_returnptr (LosuVm *vm, char *ptr)
{
  arg_return (vm, obj_newptr (vm, ptr));
}

/**
 * @brief Return a null type (void) argument to the call-stack
 * @param vm A pointer to the LosuVm
 */
LosuExtern void
arg_returnnull (LosuVm *vm)
{
  arg_return (vm, obj_newnull (vm));
}

/* obj_xxx APIs */
/**
 * @brief Convert 'LosuObj' to C-Type representation of string ( const char* )
 * @param vm A pointer to the LosuVm
 * @param obj The target object
 * @return const char* The target object's  C-Type ( const char* )
 */
LosuExtern const char *
obj_tostr (LosuVm *vm, LosuObj *obj)
{
#define tostring(o)                                                           \
  ((ovtype (o) != LosuTypeDefine_string)                                      \
   && (__losu_vmCore_Tostr (vm, o, 0) != 0))

  return (obj == NULL || tostring (obj)) ? "" : ovSstr (obj);
#undef tostring
}

/**
 * @brief Convert 'LosuObj' to C-Type representation of number (
 * _l_number )
 * @param vm A pointer to the LosuVm
 * @param obj The target object
 * @return _l_number The target object's  C-Type ( _l_number )
 */
LosuExtern _l_number
obj_tonum (LosuVm *vm, LosuObj *obj)
{
#define tonumber(o)                                                           \
  ((ovtype (o) != LosuTypeDefine_number)                                      \
   && (__losu_vmCore_Tonum (vm, o, 0)) != 0)

  return (obj == NULL || tonumber (obj)) ? 0 : ovnumber (obj);

#undef tonumber
}

/**
 * @brief Convert 'LosuObj' to C-Type representation of pointer ( char* )
 * @param vm A pointer to the LosuVm
 * @param obj The target object
 * @return char* The target object's  C-Type ( char* )
 */
LosuExtern char *
obj_toptr (LosuVm *vm, LosuObj *obj)
{
  return (ovtype (obj) == LosuTypeDefine_ptr ? obj->value.ptr : NULL);
}

/**
 * @brief Convert 'LosuObj' to C-Type representation of function ( LosuApi )
 * @param vm A pointer to the LosuVm
 * @param obj The target object
 * @return LosuApi The target object's  C-Type ( LosuApi )
 */
LosuExtern LosuApi
obj_tofunction (LosuVm *vm, LosuObj *obj)
{
  return (ovtype (obj) == LosuTypeDefine_function
              ? (ovfunc (obj)->isC ? ovfunc (obj)->func.capi : NULL)
              : NULL);
}

/**
 * @brief Get the type of 'LosuObj'
 * @param vm A pointer to the LosuVm
 * @param obj The target object
 * @return int The type of the target object
 */
LosuExtern int
obj_type (LosuVm *vm, LosuObj *obj)
{
  return ovtype (obj);
}

/**
 * @brief Get the type of 'LosuObj'
 * @param vm A pointer to the LosuVm
 * @param obj The target object
 * @return const char* The type of the target object
 */
LosuExtern const char *
obj_typeStr (LosuVm *vm, LosuObj *obj)
{

  if (ovtype (obj) < LosuTypeDefine_mark)
    {
      if (ovtype (obj) == LosuTypeDefine_unit)
        {
          LosuObj *o = obj_indexunitbystr (vm, *obj, "::type");
          if (ovtype (o) == LosuTypeDefine_string)
            return obj_tostr (vm, o);
        }
      return LosuTypeSystem[ovtype (obj)];
    }
  else
    return LosuTypeSystem[6];
}

/**
 * @brief Create a new LosuObj of type string
 * @note The string will be copied to the LosuVm's memory
 * @param vm A pointer to the LosuVm
 * @param str The string to create the LosuObj from
 * @return LosuObj The new LosuObj
 */
LosuExtern LosuObj
obj_newstr (LosuVm *vm, char *str)
{
  return (LosuObj){
    .type = LosuTypeDefine_string,
    .value.str = __losu_objString_new (vm, str),
  };
}

/**
 * @brief Create a new LosuObj of type string with length
 * @note The string will be copied to the LosuVm's memory
 * @param vm A pointer to the LosuVm
 * @param str The string to create the LosuObj from
 * @param len The length of the string
 * @return LosuObj The new LosuObj
 */
LosuExtern LosuObj
obj_newstrlen (LosuVm *vm, char *str, size_t len)
{
  return (LosuObj){
    .type = LosuTypeDefine_string,
    .value.str = __losu_objString_newstr (vm, str, len),
  };
}

/**
 * @brief Get the length of a LosuObj of type string
 * @param vm A pointer to the LosuVm
 * @param obj The target object
 * @return size_t The length of the target object
 */
LosuExtern size_t
obj_getstrlen (LosuVm *vm, LosuObj *obj)
{
  return (ovtype (obj) == LosuTypeDefine_string ? ovIstr (obj)->len : 0);
}

/**
 * @brief Create a new LosuObj of type number
 * @param vm A pointer to the LosuVm
 * @param num The number to create the LosuObj from
 * @return LosuObj The new LosuObj
 */
LosuExtern LosuObj
obj_newnum (LosuVm *vm, _l_number num)
{
  return (LosuObj){
    .type = LosuTypeDefine_number,
    .value.num = num,
  };
}

/**
 * @brief Create a new LosuObj of type function
 * @param vm A pointer to the LosuVm
 * @param func The function to create the LosuObj from
 * @return LosuObj The new LosuObj
 */
LosuExtern LosuObj
obj_newfunction (LosuVm *vm, LosuApi func)
{
  _inlineFunction *f = __losu_objFunc_new (vm, 0);
  f->isC = 1;
  f->func.capi = func;
  return (LosuObj){
    .type = LosuTypeDefine_function,
    .value.func = f,
  };
}

/**
 * @brief Create a new LosuObj of type unit
 * @param vm A pointer to the LosuVm
 * @return LosuObj The new LosuObj
 */
LosuExtern LosuObj
obj_newnull (LosuVm *vm)
{
  return (LosuObj){
    .type = LosuTypeDefine_null,
  };
}

/**
 * @brief Create a new LosuObj of type unit
 * @param vm A pointer to the LosuVm
 * @return LosuObj The new LosuObj
 */
LosuExtern LosuObj
obj_newunit (LosuVm *vm)
{
  return (LosuObj){
    .type = LosuTypeDefine_unit,
    .value.hash = __losu_objUnit_new (vm, 0),
  };
}

/**
 * @brief Create a new LosuObj of type pointer
 * @param vm A pointer to the LosuVm
 * @param ptr A byte type (const char*) argument to the call-stack
 * @note The pointer `WON'T` be copied to the LosuVm's memory
 * @return LosuObj The new LosuObj
 */
LosuExtern LosuObj
obj_newptr (LosuVm *vm, char *ptr)
{
  return (LosuObj){
    .type = LosuTypeDefine_ptr,
    .value.ptr = ptr,
  };
}

/**
 * @brief Inedx a LosuObj from target unit
 * @param vm A pointer to the LosuVm
 * @param unit The target unit
 * @param key The key to index
 * @return LosuObj The indexed object, if not found, return a LosuObj (type
 * null)
 */
LosuExtern LosuObj *
obj_indexunit (LosuVm *vm, LosuObj unit, LosuObj key)
{
  if (unit.type == LosuTypeDefine_unit)
    return (LosuObj *)__losu_objUnit_get (ovhash ((&unit)), &key);
  return (LosuObj *)&_inlineNullObj;
}

/**
 * @brief Inedx a LosuObj from target unit by number-key ( Based on API:
 * `obj_indexunit` )
 * @param vm A pointer to the LosuVm
 * @param unit The target unit
 * @param i The number-key
 * @return LosuObj The indexed object
 */
LosuExtern LosuObj *
obj_indexunitbynum (LosuVm *vm, LosuObj unit, _l_number i)
{
  if (unit.type == LosuTypeDefine_unit)
    return (LosuObj *)__losu_objUnit_getnum (ovhash ((&unit)), i);
  return (LosuObj *)&_inlineNullObj;
}

/**
 * @brief Inedx a LosuObj from target unit by string-key ( Based on API:
 * `obj_indexunit` )
 * @param vm A pointer to the LosuVm
 * @param unit The target unit
 * @param s The string-key
 * @return LosuObj The indexed object
 */
LosuExtern LosuObj *
obj_indexunitbystr (LosuVm *vm, LosuObj unit, char *s)
{
  LosuObj o = obj_newstr (vm, s);
  if (unit.type == LosuTypeDefine_unit)
    return (LosuObj *)__losu_objUnit_getstr (ovhash ((&unit)), ovIstr ((&o)));
  return (LosuObj *)&_inlineNullObj;
}

/**
 * @brief Set a LosuObj to target unit, unit[key] = value
 * @param vm A pointer to the LosuVm
 * @param unit The target unit
 * @param key The key to set
 * @param value The value to set
 */
LosuExtern void
obj_setunit (LosuVm *vm, LosuObj unit, LosuObj key, LosuObj value)
{
  if (unit.type == LosuTypeDefine_unit)
    *__losu_objUnit_set (vm, ovhash ((&unit)), &key) = value;
}

/**
 * @brief Set a LosuObj to target unit, unit[key] = value ( Based on API:
 * `obj_setunit` )
 * @param vm A pointer to the LosuVm
 * @param unit The target unit
 * @param key The key to set
 * @param value The value to set
 */
LosuExtern void
obj_setunitbynum (LosuVm *vm, LosuObj unit, _l_number key, LosuObj value)
{
  if (unit.type == LosuTypeDefine_unit)
    *__losu_objUnit_setnum (vm, ovhash ((&unit)), key) = value;
}

/**
 * @brief Set a LosuObj to target unit, unit[key] = value ( Based on API:
 * `obj_setunit` )
 * @param vm A pointer to the LosuVm
 * @param unit The target unit
 * @param key The key to set
 * @param value The value to set
 */
LosuExtern void
obj_setunitbystr (LosuVm *vm, LosuObj unit, char *key, LosuObj value)
{
  if (unit.type == LosuTypeDefine_unit)
    *__losu_objUnit_setstr (vm, ovhash ((&unit)),
                            __losu_objString_new (vm, key))
        = value;
}

/**
 * @brief Get the 'First-Node' of an unit , First-Node is the 'Logic-First' of
 * the unit, you can use API:`obj_unit_next` to get the next node, until NULL
 * @param vm A pointer to the LosuVm
 * @param unit The target unit
 * @return LosuNode* The first node of the unit, if not found, return NULL
 */
LosuExtern LosuNode *
obj_unit_first (LosuVm *vm, LosuObj unit)
{
  if (ovtype (&unit) == LosuTypeDefine_unit)
    return (LosuNode *)__losu_objUnit_getnext (ovhash ((&unit)),
                                               (LosuObj *)&_inlineNullObj);
  return NULL;
}

/**
 * @brief Locate Node with key of an unit, if not found, return NULL
 * @note This function is used to locate a node in a unit, it is not a
 * 'Always-Safe' function,it may return `NULL`
 * @param vm A pointer to the LosuVm
 * @param unit The target unit
 * @param key The key to locat
 * @return LosuNode The located node, if not found, return NULL
 */
LosuExtern LosuNode
obj_unit_location (LosuVm *vm, LosuObj unit, LosuObj key)
{
  LosuNode n;
  n.key = key;
  if (unit.type == LosuTypeDefine_unit)
    n.value = *__losu_objUnit_get (ovhash ((&unit)), &key);
  else
    n.value = _inlineNullObj;
  return n;
}

/**
 * @brief Get the next node of an unit, if not found, return NULL
 * @param vm A pointer to the LosuVm
 * @param unit The target unit
 * @param n The current node
 * @return LosuNode* The next node, if not found, return NULL
 */
LosuExtern LosuNode *
obj_unit_next (LosuVm *vm, LosuObj unit, LosuNode *n)
{
  if (unit.type == LosuTypeDefine_unit)
    return (LosuNode *)__losu_objUnit_getnext (ovhash ((&unit)), (&(n->key)));
  return NULL;
}

/**
 * @brief Get the key of an node
 * @param vm A pointer to the LosuVm
 * @param n The target node
 * @return LosuObj The key of the node
 */
LosuExtern LosuObj
obj_unit_nodekey (LosuVm *vm, LosuNode *n)
{
  return n->key;
}

/**
 * @brief Get the value of an node
 * @param vm A pointer to the LosuVm
 * @param n The target node
 * @return LosuObj The value of the node
 */
LosuExtern LosuObj
obj_unit_nodevalue (LosuVm *vm, LosuNode *n)
{
  return n->value;
}

/* Stack APIs */
/**
 * @brief Push a LosuObj to the call-stack
 * @param vm A pointer to the LosuVm
 * @param o The LosuObj to push
 */
LosuExtern void
stack_push (LosuVm *vm, LosuObj o)
{
  *(vm->top) = o;
  if (vm->top == vm->stackmax)
    vm_error (vm, LosuErrorCode_Heap,
              __config_losucore_errmsg_msgStackOverflow);
  vm->top++;
}

/**
 * @brief Pop a LosuObj from the call-stack
 * @param vm A pointer to the LosuVm
 * @param i The index of the LosuObj to pop
 * @return LosuObj The popped LosuObj
 */
LosuExtern void
stack_pop (LosuVm *vm, int i)
{
  vm->top -= i;
  if (vm->top < vm->stack)
    vm->top = vm->stack;
}

/**
 * @brief Call a function in the call-stack, NOT in `Protected Mode`, Jump to
 * the top of the stack of the current environment when an error occurs
 * @param vm A pointer to the LosuVm
 * @param argnum The number of arguments
 * @param resnum The number of results
 */
LosuExtern void
stack_call (LosuVm *vm, int argnum, int resnum)
{
  __losu_vmHeap_call (vm, vm->top - (argnum + 1), resnum);
}

/* Charset API s */
#ifdef __config_losucore_encode_winGBK
#include <windows.h>
#endif

/**
 * @brief Convert a Losu(UTF-8) string to Sys(GBK/UTF8) string
 * @note This function is used to convert a UTF-8 string to GBK string, it will
 * malloc dynamic memory, so you need to free it after use. If Sys-charset is
 * UTF8 too, it will return the same char*.
 * @param s The UTF-8 string to convert
 * @return char* The Sys string, if failed, return NULL.
 */
LosuExtern char *
charset_toSys (const char *s)
{
#ifdef __config_losucore_encode_nativeUTF8
  return (char *)s;
#endif

#ifdef __config_losucore_encode_winGBK
  return __losuWintoGBK (s);
#endif
}

/**
 * @brief Convert a Sys(GBK/UTF8) string to Losu(UTF-8) string
 * @note This function is used to convert a GBK string to UTF-8 string, it will
 * malloc dynamic memory, so you need to free it after use.If Sys-charset is
 * UTF8 too, it will return the same char*
 * @param s The GBK string to convert
 * @return char* The UTF-8 string, if failed, return NULL
 */
LosuExtern char *
charset_toLosu (const char *s)
{
#ifdef __config_losucore_encode_nativeUTF8
  return (char *)s;
#endif

#ifdef __config_losucore_encode_winGBK
  return __losuWintoUTF8 (s);
#endif
}

/**
 * @brief Create a new LosuObj from a GBK string, and convert to UTF-8
 * @note This function is used to create a new LosuObj from a GBK string, and
 * convert to UTF-8, it will use LosuVm memory pool to allocate memory, so you
 * don't need to free it after use when Losu's GC is working
 * @param vm A pointer to the LosuVm
 * @param s The UTF-8 string to convert
 * @return char* The string C-Type of new LosuObj ( char* ), if failed, return
 * NULL
 */
LosuExtern char *
charset_newObj_toLosu (LosuVm *vm, const char *s)
{
  char *t = charset_toLosu (s);
  if (t == NULL)
    return NULL;
  LosuObj o = obj_newstr (vm, t);
  if (t != s)
    __losu_mem_free (NULL, t);
  return ovSstr ((&o));
}

/**
 * @brief Create a new LosuObj from a UTF-8 string, and convert to GBK
 * @note This function is used to create a new LosuObj from a UTF-8 string, and
 * convert to GBK, it will use LosuVm memory pool to allocate memory, so you
 * don't need to free it after use when Losu's GC is working
 * @param vm A pointer to the LosuVm
 * @param s The UTF-8 string to convert
 * @return char* The string C-Type of new LosuObj ( char* ), if failed, return
 * NULL
 */
LosuExtern char *
charset_newObj_toSys (LosuVm *vm, const char *s)
{
  char *t = charset_toSys (s);
  if (t == NULL)
    return NULL;
  LosuObj o = obj_newstr (vm, t);
  if (t != s)
    __losu_mem_free (NULL, t);
  return ovSstr ((&o));
}

#endif

#ifndef define_source_losu_inline
#define define_source_losu_inline

#ifdef __config_losucore_encode_winGBK
#include <wchar.h>
#include <windows.h>
static char *
__losuWintoGBK (const char *str)
{
  int len = MultiByteToWideChar (CP_UTF8, 0, str, -1, NULL, 0);
  wchar_t *strUnicode
      = (wchar_t *)__losu_mem_malloc (NULL, len * sizeof (wchar_t *));
  wmemset (strUnicode, 0, len);
  MultiByteToWideChar (CP_UTF8, 0, str, -1, strUnicode, len);
  len = WideCharToMultiByte (CP_ACP, 0, strUnicode, -1, NULL, 0, NULL, NULL);
  char *strGbk = (char *)__losu_mem_malloc (NULL, len * sizeof (char *));
  memset (strGbk, 0, len);
  WideCharToMultiByte (CP_ACP, 0, strUnicode, -1, strGbk, len, NULL, NULL);
  __losu_mem_free (NULL, strUnicode);
  strUnicode = NULL;
  return strGbk;
}
static char *
__losuWintoUTF8 (const char *str)
{
  int len = MultiByteToWideChar (CP_ACP, 0, str, -1, NULL, 0);
  wchar_t *strUnicode
      = (wchar_t *)__losu_mem_malloc (NULL, len * sizeof (wchar_t *));
  wmemset (strUnicode, 0, len);
  MultiByteToWideChar (CP_ACP, 0, str, -1, strUnicode, len);
  len = WideCharToMultiByte (CP_UTF8, 0, strUnicode, -1, NULL, 0, NULL, NULL);
  char *strutf8 = (char *)__losu_mem_malloc (NULL, len * sizeof (char *));
  memset (strutf8, 0, len);
  WideCharToMultiByte (CP_UTF8, 0, strUnicode, -1, strutf8, len, NULL, NULL);
  __losu_mem_free (NULL, strUnicode);
  strUnicode = NULL;
  return strutf8;
}

#endif

static int32_t
__losuApigetline (LosuVm *vm, LosuObj *obj)
{
  while (1)
    {
      /* printf ("stack:%p\t%p\n", vm->stack, obj); */
      if (!(obj && ovtype (obj) == LosuTypeDefine_mark
            && !ovcall (obj)->func->isC))
        obj--;
      else
        break;
      if (obj < vm->stack)
        return -1;
    }

  return __losuApitryline (ovcall (obj)->func->func.sdef->lineinfo,
                           __losuApicurrentpc (obj));
}

static int32_t
__losuApicurrentpc (LosuObj *obj)
{

  if (ovcall (obj)->pc)
    return (*(ovcall (obj)->pc) - ovcall (obj)->func->func.sdef->code) - 1;
  else
    return -1;
}
static int32_t
__losuApitryline (int32_t *lineinfo, int32_t pc)
{

  int32_t refi = 0;
  int32_t refline = 1;
  if (lineinfo == NULL || pc == -1)
    return -1;
  if (lineinfo[refi] < 0)
    refline += -lineinfo[refi++];
  while (lineinfo[refi] > pc)
    {
      refline--;
      refi--;
      if (lineinfo[refi] < 0)
        refline -= -lineinfo[refi++];
    }

  while (1)
    {
      int32_t nextline = refline + 1;
      int32_t nexref = refi + 1;
      if (lineinfo[nexref] < 0)
        nextline += -lineinfo[nexref++];
      if (lineinfo[nexref] > pc)
        break;
      refline = nextline;
      refi = nexref;
    }

  return refline;
}

#endif
